package com.example.springbootdemo.util;

import org.apache.commons.lang3.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Optional;
import java.util.function.Predicate;

public class WebUtil {

    /**
     * IP 头部名称数组
     * <p>
     * x-forwarded-for：代理服务器转发的客户端 IP
     * X-Real-IP：某些代理服务器使用的真实客户端 IP
     * CF-Connecting-IP：Cloudflare 代理服务器提供的客户端 IP
     * Proxy-Client-IP：HTTP 代理或负载均衡服务器 IP
     * WL-Proxy-Client-IP：WebLogic 代理的客户端 IP
     * HTTP_CLIENT_IP：有些代理服务器会加上此请求头部
     * HTTP_X_FORWARDED_FOR：有些代理服务器会加上此请求头部
     */
    private static final String[] IP_HEADER_NAMES = new String[]{
            "x-forwarded-for",
            "X-Real-IP",
            "CF-Connecting-IP",
            "Proxy-Client-IP",
            "WL-Proxy-Client-IP",
            "HTTP_CLIENT_IP",
            "HTTP_X_FORWARDED_FOR"
    };

    /**
     * IP 地址的断言条件
     */
    private static final Predicate<String> IP_PREDICATE = (ip) -> StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip);

    public static String getIp() {
        return getIp(getRequest());
    }

    public static String getIp(HttpServletRequest request) {
        if (request == null) {
            return "";
        }

        String ip = Arrays.stream(IP_HEADER_NAMES)
                .map(request::getHeader)
                .filter(ele -> !IP_PREDICATE.test(ele))
                .findFirst()
                .orElse(null);
        if (IP_PREDICATE.test(ip)) {
            // 如果遍历 IP 头部名称数组后仍未找到有效的 IP 地址，则从 RemoteAddr 中获取 IP 地址
            ip = request.getRemoteAddr();
        }
        return StringUtils.isBlank(ip) ? null : StringUtils.split(ip, ",")[0];
    }

    public static HttpServletRequest getRequest() {
        RequestAttributes requestAttributes = Optional.ofNullable(RequestContextHolder.getRequestAttributes())
                .orElseThrow(() -> new RuntimeException("request is null"));
        return ((ServletRequestAttributes) requestAttributes).getRequest();
    }
}
